package ph.com.gs3.formalistics.presenter.fragments.views;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.JSONException;
import org.json.JSONObject;

import ph.com.gs3.formalistics.R;
import ph.com.gs3.formalistics.global.utilities.logging.FLLogger;
import ph.com.gs3.formalistics.model.valueobjects.business.FieldProperties;
import ph.com.gs3.formalistics.model.valueobjects.business.document.AbstractDocument;
import ph.com.gs3.formalistics.model.valueobjects.business.document.Document.DocumentStar;
import ph.com.gs3.formalistics.model.valueobjects.business.form.Form;
import ph.com.gs3.formalistics.model.valueobjects.business.form.FormField;
import ph.com.gs3.formalistics.model.valueobjects.business.form.FormField.FieldType;
import ph.com.gs3.formalistics.presenter.DocumentActivity;
import ph.com.gs3.formalistics.presenter.DocumentActivity.DocumentType;
import ph.com.gs3.formalistics.view.custom.fields.BarcodeScannerField;
import ph.com.gs3.formalistics.view.custom.fields.DateTimePickerField;
import ph.com.gs3.formalistics.view.documentview.DocumentViewDataExtractor;
import ph.com.gs3.formalistics.view.documentview.FieldBuilder;
import ph.com.gs3.formalistics.view.documentview.FieldRequiredNotifier;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class DocumentViewFragment extends Fragment {

	// Constants
	public static String TAG = DocumentViewFragment.class.getSimpleName();

	public static final int DRAWABLE_STAR = R.drawable.starstrokestarred;
	public static final int DRAWABLE_UNSTAR = R.drawable.starstrokelight;

	public static final int ACTION_STAR = R.string.action_star_document;
	public static final int ACTION_UNSTAR = R.string.action_unstar_document;

	// View Fields
	private LinearLayout llActionsContainer;
	private LinearLayout llFieldsContainer;
	private LinearLayout llStarBusyPrompt;
	private LinearLayout llMessageContainer;

	private TextView tvMessage;

	private ProgressBar pbMessageProgress;

	private Button bMarkDocumentStar;
	private Button bOpenComments;

	// Data Fields
	private Form form;
	private AbstractDocument document;

	private DocumentActivity.DocumentType documentType;

	private DocumentViewActionListener listener;

	private FieldRequiredNotifier fieldRequiredNotifier;
	private Map<String, FieldType> allFieldsType;

	public static DocumentViewFragment createInstance(Form form, AbstractDocument document,
	        DocumentActivity.DocumentType documentType) {
		
		DocumentViewFragment instance = new DocumentViewFragment();
		instance.form = form;
		instance.document = document;

		instance.allFieldsType = new HashMap<>();

		instance.documentType = documentType;
		
		return instance;

	}

	// required constructor without parameters
	public DocumentViewFragment() {}

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);

		try {
			listener = (DocumentViewActionListener) activity;
		} catch (ClassCastException e) {
			throw new IllegalStateException(activity.getClass().getSimpleName()
			        + " does not implement DocumentViewActionListener");
		}
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		View rootView = inflater.inflate(R.layout.fragment_document, container, false);

		initializeDefaultViews(rootView);
		initializeFormFields();
		fieldRequiredNotifier = new FieldRequiredNotifier(rootView);

		return rootView;
	}

	private void initializeDefaultViews(View rootView) {
		llActionsContainer = (LinearLayout) rootView.findViewById(R.id.Document_llActionsContainer);
		llFieldsContainer = (LinearLayout) rootView.findViewById(R.id.Document_llFieldsContainer);
		llMessageContainer = (LinearLayout) rootView.findViewById(R.id.Document_llMessageContainer);

		llStarBusyPrompt = (LinearLayout) rootView
		        .findViewById(R.id.Document_llMarkDocumentStarBusy);

		tvMessage = (TextView) rootView.findViewById(R.id.Document_tvMessage);

		pbMessageProgress = (ProgressBar) rootView.findViewById(R.id.Document_pbMessageProgress);

		bMarkDocumentStar = (Button) rootView.findViewById(R.id.Document_bMarkDocumentStar);
		bOpenComments = (Button) rootView.findViewById(R.id.Document_bOpenComments);

		bOpenComments.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				listener.onOpenCommentsCommand();
				getActivity().overridePendingTransition(R.animator.push_up_in,
				        R.animator.push_up_out);
			}

		});

		bMarkDocumentStar.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {

				if (document != null) {

					displayProcessingStarMark();

					// inverse of the current star mark
					DocumentStar star = document.isStarred() ? DocumentStar.UNSTARRED
					        : DocumentStar.STARRED;
					listener.onMarkDocumentStarCommand(star);

				} else {
					Toast.makeText(getActivity(),
					        "You cannot mark unsubmitted documents as starred", Toast.LENGTH_LONG)
					        .show();
				}

			}
		});

		if (documentType == DocumentType.NEW_DOCUMENT
		        || documentType == DocumentType.OUTGOING_DOCUMENT) {
			hideActions();
		} else if (documentType == DocumentType.EXISTING_DOCUMENT) {
			displayActions();

			if (document != null) {
				if (document.isStarred()) {
					displayDocumentAsStarred();
				} else {
					displayDocumentAsUnstarred();
				}

			}
		}

	}

	private void initializeFormFields() {

		FieldBuilder builder = new FieldBuilder(getActivity(), form.getServer());

		JSONObject fieldValues = new JSONObject();

		// if (document != null && !"0".equals(document.getWebId())) {
		if (document != null && document.getFields() != null) {
			fieldValues = document.getFields();
		}

		// initialize field properties

		List<String> fieldsEnabled = null;
		List<String> fieldsHidden = null;
		List<String> fieldsRequired = null;

		if (AbstractDocument.DEFAULT_UNSUBMITTED_DOCUMENT_WEB_ID.equals(document.getWebId())) {
			// new document, get the field properties from the form
			fieldsEnabled = form.getOnCreateFieldsEnabled();
			fieldsHidden = form.getOnCreateFieldsHidden();
			fieldsRequired = form.getOnCreateFieldsRequired();
		} else {
			// existing document on the workflow, get the field properties from the
			// document
			fieldsEnabled = document.getFieldsEnabled();
			fieldsHidden = document.getFieldsHidden();
			fieldsRequired = document.getFieldsRequired();
		}

		// initialize listeners using the activity,
		// this may throw an illegal state exception if the required listeners are not
		// implemented by the activity
		builder.setSpecialFieldsListener(getActivity());

		// create the fields one by one then add them to the view
		for (FormField field : form.getFields()) {

			// get and assign the value of the field
			if (fieldValues.has(field.getId())) {
				try {
					String value = fieldValues.getString(field.getId());
					field.setValue(value);
				} catch (JSONException e) {
					FLLogger.d(TAG,
					        "Unable to read value of field " + field.getId() + " " + e.getMessage());
					e.printStackTrace();
				}
			}

			// generate the properties of the field from the form
			FieldProperties properties = generateProperties(field.getId(), fieldsEnabled,
			        fieldsHidden, fieldsRequired);
			View fieldView = builder.createField(field, properties);

			if (fieldView != null) {
				llFieldsContainer.addView(fieldView);
				// save the field's type
				allFieldsType.put(field.getId(), field.getType());
			} else {
				FLLogger.d(TAG, "Unable to create field for " + field.getId()
				        + ", it's probably unsupported");
			}

		}
	}

	// ====================================================================================
	// {{ Functional Methods

	public void updateFieldValue(FormField.FieldType fieldType, String fieldId, String fieldValue) {

		switch (fieldType) {
			case TEXT_FIELD:
			case TEXT_AREA: {
				EditText etField = (EditText) llFieldsContainer.findViewWithTag(fieldId);
				etField.setText(fieldValue);
			}
				break;
			default:
				break;
		}

	}

	public void updateDateTimeFieldValue(String fieldId, String value) {

		try {
			DateTimePickerField field = (DateTimePickerField) llFieldsContainer
			        .findViewWithTag(fieldId + "_container");

			field.setValue(value);

		} catch (ClassCastException e) {
			FLLogger.e(TAG,
			        "Tried to update field as a DateTimePickerField but it's not. Update failed");
		}

	}

	public void updateBarcodeScannerFieldValue(String fieldId, String value) {

		try {
			BarcodeScannerField field = (BarcodeScannerField) llFieldsContainer
			        .findViewWithTag(fieldId + "_container");
			field.setScannedCode(value);
		} catch (ClassCastException e) {
			FLLogger.e(TAG,
			        "Tried to update field as a BarcodeScannerField but it's not. Update failed");
		}
	}

	public void displayMessage(String message, boolean displayProgressBar) {

		llMessageContainer.setVisibility(View.VISIBLE);
		pbMessageProgress.setVisibility(displayProgressBar ? View.VISIBLE : View.GONE);
		tvMessage.setText(message);

	}

	public void dismissMessage() {

		// reset the views to their defaults
		llMessageContainer.setVisibility(View.GONE);
		pbMessageProgress.setVisibility(View.GONE);
		tvMessage.setText("");

	}

	public JSONObject getUpdatedFieldValuesFromView() {

		DocumentViewDataExtractor extractor = new DocumentViewDataExtractor();
		return extractor.extract(llFieldsContainer);

	}

	public void notifyFieldsRequired(List<String> fieldsThatFailedValidation) {

		for (String field : fieldsThatFailedValidation) {
			FLLogger.d(TAG, field + " failed validation");
			if (allFieldsType.containsKey(field)) {
				FieldType fieldType = allFieldsType.get(field);
				fieldRequiredNotifier.notifyField(field, fieldType);
			}
		}

	}

	public FieldProperties generateProperties(String fieldId, List<String> fieldsEnabled,
	        List<String> fieldsHidden, List<String> fieldsRequired) {

		FieldProperties properties = new FieldProperties();

		if (fieldsEnabled != null) {
			properties.setEnabled(fieldsEnabled.contains(fieldId));
			properties.setHidden(fieldsHidden.contains(fieldId));
			properties.setRequired(fieldsRequired.contains(fieldId));
		} else {
			FLLogger.e(TAG, "Field properties not set!");
		}

		return properties;

	}

	// {{ Document Star Related Functions

	public void displayProcessingStarMark() {
		bMarkDocumentStar.setVisibility(View.GONE);
		llStarBusyPrompt.setVisibility(View.VISIBLE);
	}

	public void displayDocumentAsStarred() {
		llStarBusyPrompt.setVisibility(View.GONE);
		bMarkDocumentStar.setVisibility(View.VISIBLE);

		FLLogger.d(TAG, "setting as starred");
		bMarkDocumentStar.setText(ACTION_UNSTAR);
		bMarkDocumentStar.setCompoundDrawablesWithIntrinsicBounds(DRAWABLE_STAR, 0, 0, 0);

	}

	public void displayDocumentAsUnstarred() {
		llStarBusyPrompt.setVisibility(View.GONE);
		bMarkDocumentStar.setVisibility(View.VISIBLE);

		bMarkDocumentStar.setText(ACTION_STAR);
		bMarkDocumentStar.setCompoundDrawablesWithIntrinsicBounds(DRAWABLE_UNSTAR, 0, 0, 0);

	}

	public void displayErrorMarkingDocumentStar() {

		Toast.makeText(getActivity(),
		        "There was an error starring/unstarring your document, please try again later",
		        Toast.LENGTH_LONG).show();

		if (document.isStarred()) {
			displayDocumentAsStarred();
		} else {
			displayDocumentAsUnstarred();
		}

	}

	// }}

	// {{ Document Actions Related Functions

	public void displayActions() {
		llActionsContainer.setVisibility(View.VISIBLE);
	}

	public void hideActions() {
		llActionsContainer.setVisibility(View.GONE);
	}

	// }}

	// }}

	public static interface DocumentViewActionListener {
		public void onMarkDocumentStarCommand(DocumentStar star);

		public void onOpenCommentsCommand();
	}

}
